#include "SkillModel.h"
#include <QDebug>
#include <QTime>
#include <cstdlib> // 引入 cstdlib 来使用 srand/rand
#include <QMap>
#include <QUuid>
#include <QTimer>
#include <QSettings>
#include <QStandardPaths>
#include <QDir>
#include <QSet>
#include <QFileInfo>
#include <QUrl>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QJsonParseError>
#include <QFile>
#include <QCryptographicHash>
#include <QRegularExpression>
#include <algorithm>

SkillModel::SkillModel(QObject *parent)
    : QAbstractListModel(parent)
    , m_networkManager(new QNetworkAccessManager(this))
    , m_autoSyncEnabled(false)
    , m_cloudSyncValid(false)
    , m_dataModified(false)
{
    // 修复：使用 std::srand 替代已弃用的 qsrand，并使用 QTime 作为种子
    std::srand(QTime::currentTime().msec());

    // 尝试加载保存的数据
    loadData();
    
    // 如果没有数据，则初始化默认数据
    if (m_skillTrees.isEmpty()) {
        initializeDefaultChildrenSkills();

        // 通知UI更新
        emit countChanged();
        emit currentTreeIdChanged();

        saveData();  // 立即保存
        clearModifiedFlag();  // 清除修改标志，因为这是初始化数据，不是用户修改
    } else {
        // 如果加载了旧数据，强制重新布局以应用新的间距参数
        QString savedCurrentTreeId = m_currentTreeId;
        for (auto it = m_skillTrees.begin(); it != m_skillTrees.end(); ++it) {
            m_currentTreeId = it.key();
            // 静默重新布局（不发出信号）
            SkillTree& tree = m_skillTrees[it.key()];
            if (!tree.skills.isEmpty()) {
                rearrangeSkillTree();
            }
        }
        // 恢复当前树
        m_currentTreeId = savedCurrentTreeId;
        // 保存更新后的坐标
        saveData();
        clearModifiedFlag();  // 清除修改标志，因为这是布局调整，不是用户修改
    }

    updatePlayerStats();
}

// SkillModel::SkillModel(QObject *parent)
//     : QAbstractListModel(parent)
// {
//     qDebug() << "SkillModel constructor started";

//     std::srand(static_cast<unsigned int>(QDateTime::currentMSecsSinceEpoch()));

//     // 先创建技能树
//     QString defaultId = createNewSkillTree("感知系统基础");
//     qDebug() << "Default skill tree created with ID:" << defaultId;

//     if (!defaultId.isEmpty()) {
//         setCurrentTreeId(defaultId);
//         qDebug() << "Current tree ID set";

//         // 修复：使�?Lambda 表达式确保连接成�?//         QTimer::singleShot(0, this, [this]() {
//             qDebug() << "Lambda timer triggered";
//             this->initializeDefaultSkills();
//         });
//     }

//     qDebug() << "SkillModel constructor completed";
// }

// 初始化儿童技能树
void SkillModel::initializeDefaultChildrenSkills()
{
    qDebug() << "开始初始化默认技能树...";
    
    // ==================== 1. 数学技能树 ====================
    QString mathId = createNewSkillTree("📐 数学", "数学", false);
    setCurrentTreeId(mathId);
    
    // 数学技能节点
    addSkill("认识数字 1-10", -1, "数学", "学会认识并写出数字1到10", 1);
    addSkill("认识数字 1-100", 1, "数学", "学会认识并写出数字1到100", 2);
    addSkill("十以内加法", 1, "数学", "能够计算10以内的加法", 2);
    addSkill("十以内减法", 1, "数学", "能够计算10以内的减法", 2);
    addSkill("二十以内加法", 3, "数学", "能够计算20以内的加法", 3);
    addSkill("二十以内减法", 4, "数学", "能够计算20以内的减法", 3);
    addSkill("认识钟表", 1, "数学", "学会看时钟，认识整点和半点", 2);
    addSkill("简单乘法", 5, "数学", "学会九九乘法表", 3);
    addSkill("简单除法", 8, "数学", "理解除法的概念", 3);
    unlockSkill(1);  // 默认解锁第一个技能
    
    qDebug() << "✅ 数学技能树创建完成";
    
    // ==================== 2. 语文技能树 ====================
    QString chineseId = createNewSkillTree("📚 语文", "语文", false);
    setCurrentTreeId(chineseId);
    
    // 语文技能节点
    addSkill("认识拼音", -1, "语文", "学会所有拼音字母", 1);
    addSkill("拼读音节", 1, "语文", "能够拼读简单的音节", 2);
    addSkill("认识汉字 10个", -1, "语文", "认识并会写10个基础汉字", 1);
    addSkill("认识汉字 50个", 3, "语文", "认识并会写50个基础汉字", 2);
    addSkill("认识汉字 100个", 4, "语文", "认识并会写100个基础汉字", 3);
    addSkill("会写自己的名字", 3, "语文", "能够正确书写自己的名字", 1);
    addSkill("背诵古诗 5首", 3, "语文", "能够背诵5首简单的古诗", 2);
    addSkill("背诵古诗 10首", 7, "语文", "能够背诵10首简单的古诗", 3);
    addSkill("阅读绘本", 5, "语文", "能够独立阅读简单的绘本", 2);
    unlockSkill(1);  // 默认解锁第一个技能
    
    qDebug() << "✅ 语文技能树创建完成";
    
    // ==================== 3. 英语技能树 ====================
    QString englishId = createNewSkillTree("🔤 英语", "英语", false);
    setCurrentTreeId(englishId);
    
    // 英语技能节点
    addSkill("认识26个字母", -1, "英语", "学会26个英文字母", 1);
    addSkill("字母发音", 1, "英语", "能够正确发音26个字母", 2);
    addSkill("简单单词 10个", 1, "英语", "学会10个简单的英文单词", 2);
    addSkill("简单单词 30个", 3, "英语", "学会30个简单的英文单词", 3);
    addSkill("简单单词 50个", 4, "英语", "学会50个简单的英文单词", 4);
    addSkill("日常问候语", 3, "英语", "会说Hello, Thank you等问候语", 2);
    addSkill("认识颜色英文", 1, "英语", "学会red, blue, green等颜色单词", 2);
    addSkill("认识数字英文", 1, "英语", "学会one到ten的英文", 2);
    addSkill("简单句子", 6, "英语", "能够说简单的英文句子", 4);
    unlockSkill(1);  // 默认解锁第一个技能
    
    qDebug() << "✅ 英语技能树创建完成";
    
    // ==================== 4. 跳绳技能树 ====================
    QString jumpRopeId = createNewSkillTree("🎽 跳绳", "运动", false);
    setCurrentTreeId(jumpRopeId);
    
    // 跳绳技能节点
    addSkill("学会单摇跳绳", -1, "运动", "掌握基本的单摇跳绳动作", 1);
    addSkill("连续跳10个", 1, "运动", "能够连续跳绳10个不中断", 2);
    addSkill("连续跳30个", 2, "运动", "能够连续跳绳30个不中断", 2);
    addSkill("连续跳50个", 3, "运动", "能够连续跳绳50个不中断", 3);
    addSkill("连续跳100个", 4, "运动", "能够连续跳绳100个不中断", 3);
    addSkill("连续跳200个", 5, "运动", "能够连续跳绳200个不中断", 4);
    addSkill("学会双摇跳绳", 5, "运动", "掌握双摇跳绳技巧", 4);
    addSkill("一分钟跳100个", 5, "运动", "在一分钟内跳绳100个", 4);
    addSkill("花样跳绳", 7, "运动", "能够进行交叉跳、侧甩跳等花样", 5);
    unlockSkill(1);  // 默认解锁第一个技能
    
    qDebug() << "✅ 跳绳技能树创建完成";
    
    // 设置数学技能树为默认显示
    setCurrentTreeId(mathId);
    
    qDebug() << "🎉 所有技能树初始化完成！共创建了4个技能树";
}

// QML 可调用：创建新的技能树
QString SkillModel::createNewSkillTree(const QString& name, const QString& category, bool autoCreateRootNode)
{
    QUuid newUuid = QUuid::createUuid();
    // 修复 Id12 错误：使用 QUuid::WithoutBraces
    QString idString = newUuid.toString(QUuid::WithoutBraces);

    SkillTree newTree;
    newTree.id = newUuid;
    newTree.name = name;
    newTree.category = category;
    newTree.nextId = 1; // 技能 ID 从 1 开始

    m_skillTrees.insert(idString, newTree);

    // 如果需要自动创建根节点
    if (autoCreateRootNode) {
        // 保存当前的技能树ID
        QString previousTreeId = m_currentTreeId;
        
        // 临时切换到新技能树以添加根节点（不触发信号）
        m_currentTreeId = idString;
        
        // 自动创建同名根节点
        addSkill(name, -1, category, "根节点", 1);
        
        // 恢复之前的技能树ID（如果有的话）
        if (!previousTreeId.isEmpty() && previousTreeId != idString) {
            m_currentTreeId = previousTreeId;
        } else if (m_skillTrees.size() == 1 || previousTreeId.isEmpty()) {
            // 如果这是第一个技能树，保持当前ID并触发信号
            emit currentTreeIdChanged();
            emit countChanged();
        }
    } else {
        // 不自动创建根节点时，如果是第一个技能树，设置为当前
        if (m_skillTrees.size() == 1 || m_currentTreeId.isEmpty()) {
            setCurrentTreeId(idString);
        }
    }

    return idString;
}

// QML 可调用：获取所有技能树列表
QVariantList SkillModel::getSkillTrees() const
{
    QVariantList list;
    for (auto it = m_skillTrees.constBegin(); it != m_skillTrees.constEnd(); ++it) {
        list.append(QVariantMap({
            {"id", it.key()},
            {"name", it.value().name},
            {"category", it.value().category}
        }));
    }
    return list;
}

// QML 可调用：删除技能树
void SkillModel::deleteSkillTree(const QString& treeId)
{
    
    // 检查技能树是否存在
    if (!m_skillTrees.contains(treeId)) {
        qWarning() << "deleteSkillTree: Tree not found with ID:" << treeId;
        return;
    }
    
    // 至少保留一个技能树
    if (m_skillTrees.size() <= 1) {
        qWarning() << "deleteSkillTree: Cannot delete the last skill tree";
        return;
    }
    
    // 如果要删除的是当前技能树，需要先切换到另一个
    if (m_currentTreeId == treeId) {
        
        // 找到第一个不是当前树的技能树
        for (auto it = m_skillTrees.constBegin(); it != m_skillTrees.constEnd(); ++it) {
            if (it.key() != treeId) {
                setCurrentTreeId(it.key());
                break;
            }
        }
    }
    
    // 删除技能树
    m_skillTrees.remove(treeId);
    
    // 标记数据已修改
    markDataAsModified();
    // 保存数据
    saveData();

    updatePlayerStats();
}

// QML 可调用：添加技能到当前技能树
void SkillModel::addSkill(QString name, int parentId, QString category, QString description, int difficulty, bool isLevel, int maxStarRating, QString medalIcon)
{
    
    if (m_currentTreeId.isEmpty() || !m_skillTrees.contains(m_currentTreeId)) {
        qWarning() << "Cannot add skill: No active skill tree selected.";
        return;
    }
    
    // 检查层级深度（最大50层）
    if (parentId != -1) {
        int depth = 0;
        int currentParent = parentId;
        QSet<int> visitedParents; // 防止循环引用导致无限循环
        
        while (currentParent != -1 && depth < 100) {
            // 检查是否存在循环
            if (visitedParents.contains(currentParent)) {
                emit syncFailed("检测到循环引用，无法添加节点");
                return;
            }
            visitedParents.insert(currentParent);
            
            // 查找父节点
            bool found = false;
            for (const Skill& skill : currentSkillTree().skills) {
                if (skill.id == currentParent) {
                    currentParent = skill.parent;
                    depth++;
                    found = true;
                    break;
                }
            }
            
            if (!found) {
                emit syncFailed("父节点不存在，无法添加节点");
                return;
            }
        }
        
        if (depth >= 50) {
            emit syncFailed("技能树层级不能超过50层");
            return;
        }
    }
    
    // 恢复beginInsertRows调用
    beginInsertRows(QModelIndex(), currentSkillTree().skills.size(), currentSkillTree().skills.size());
    
    difficulty = std::max(1, difficulty);
    bool levelNode = isLevel;
    int clampedMaxStars = qBound(1, maxStarRating, 5);
    QString normalizedMedalIcon = medalIcon.trimmed();
    if (!levelNode) {
        normalizedMedalIcon.clear();
    } else if (normalizedMedalIcon.isEmpty()) {
        normalizedMedalIcon = QStringLiteral("🏅");
    }
    
    // 计算技能位置
    int xPos, yPos;
    
    if (parentId == -1) {
        // 根节点位置：始终在页面底部中间
        // 检查是否已有根节点
        bool hasRoot = false;
        int maxRootX = 0;
        for (const Skill& skill : currentSkillTree().skills) {
            if (skill.parent == -1) {
                hasRoot = true;
                maxRootX = std::max(maxRootX, skill.x);
            }
        }
        
        if (hasRoot) {
            // 如果已有根节点，在其右侧放置新根节点
            xPos = maxRootX + 300;
        } else {
            // 第一个根节点：固定在页面底部中间位置
            xPos = 1000; // 水平居中位置
        }
        
        yPos = 2000; // Y坐标固定为2000（页面底部）
        
    } else {
        // 子节点位置：在父节点上方
        // 找到父技能
        Skill* parentSkill = nullptr;
        for (int i = 0; i < currentSkillTree().skills.size(); i++) {
            if (currentSkillTree().skills[i].id == parentId) {
                parentSkill = &currentSkillTree().skills[i];
                break;
            }
        }
        
        if (parentSkill) {
            // 计算子节点应该在的Y坐标（父节点上方）
            int childY = parentSkill->y - 70; // 垂直间距（原值150的约一半）
            yPos = childY;
            
            // 统计当前父节点已有的子节点数量和位置
            int siblingCount = 0;
            QList<int> siblingXPositions;
            
            for (const Skill& skill : currentSkillTree().skills) {
                if (skill.parent == parentId) {
                    siblingCount++;
                    siblingXPositions.append(skill.x);
                }
            }
            
            if (siblingCount == 0) {
                // 这是该父节点的第一个子节点
                // 检查同一Y层级是否有其他父节点的子节点
                int maxOccupiedX = -999999;
                bool hasOtherNodesInLayer = false;
                
                for (const Skill& skill : currentSkillTree().skills) {
                    // 检查是否在同一Y层级但不是同一父节点
                    if (std::abs(skill.y - childY) < 50 && skill.parent != parentId) {
                        hasOtherNodesInLayer = true;
                        maxOccupiedX = std::max(maxOccupiedX, skill.x + 60);
                    }
                }
                
                if (hasOtherNodesInLayer) {
                    // 在其他节点组的右侧，留出足够间距（新组的开始）
                    xPos = maxOccupiedX + 10; // 组间距（原值20的一半）
                } else {
                    // 该层级的第一个节点，以父节点为基准
                    xPos = parentSkill->x - 50;
                }
            } else {
                // 这不是第一个子节点，应该紧挨着同父节点的兄弟节点
                // 找出最右侧的兄弟节点位置
                int maxSiblingX = *std::max_element(siblingXPositions.begin(), siblingXPositions.end());
                xPos = maxSiblingX + 60; // 横向间距（原值125的约一半）
            }
            
        } else {
            // 如果找不到父技能，使用默认位置
            qWarning() << "Parent skill not found for parentId:" << parentId;
            xPos = 500 + (std::rand() % 200) - 100;
            yPos = 1500 - (currentSkillTree().nextId * 70);
        }
    }


    currentSkillTree().skills.append({
        currentSkillTree().nextId,
        name,
        parentId,
        xPos,
        yPos,
        false, // 默认未解锁
        false, // 默认未完成
        false, // 默认未选中
        category,
        description,
        difficulty,
        0, // 默认进度为0
        0,  // 默认星级为0（未完成）
        levelNode,
        clampedMaxStars,
        normalizedMedalIcon
    });
    
    int newSkillId = currentSkillTree().nextId;
    currentSkillTree().nextId++;
    
    endInsertRows();
    
    // 添加节点后重新排列整个技能树，使其美观、对称
    rearrangeSkillTree();
    
    // 标记数据已修改
    markDataAsModified();
    
    // 自动保存
    saveData();
    
    // 发出countChanged信号
    emit countChanged();
}

// QML 可调用：解锁技能
void SkillModel::unlockSkill(int skillId)
{
    
    if (m_currentTreeId.isEmpty() || !m_skillTrees.contains(m_currentTreeId)) {
        return;
    }

    SkillTree& tree = currentSkillTree();
    
    for (int i = 0; i < tree.skills.size(); i++) {
        if (tree.skills[i].id == skillId) {
            
            if (tree.skills[i].unlocked) {
                return; // 已经解锁
            }

            bool canUnlock = true;
            if (tree.skills[i].parent != -1) {
                // 检查父节点是否已完成
                bool parentCompleted = false;
                for (const Skill &s : tree.skills) {
                    if (s.id == tree.skills[i].parent) {
                        parentCompleted = s.completed;
                        break;
                    }
                }
                canUnlock = parentCompleted;
            }

            if (canUnlock) {
                tree.skills[i].unlocked = true;
                QModelIndex idx = index(i, 0);
                emit dataChanged(idx, idx, {UnlockedRole});
                
                // 标记数据已修改
                markDataAsModified();
                // 自动保存
                saveData();
            } else {
            }
            return;
        }
    }
    
}

// QML 可调用：完成技能
void SkillModel::completeSkill(int skillId)
{
    if (m_currentTreeId.isEmpty() || !m_skillTrees.contains(m_currentTreeId)) {
        return;
    }

    SkillTree& tree = currentSkillTree();
    for (int i = 0; i < tree.skills.size(); i++) {
        if (tree.skills[i].id == skillId) {
            if (tree.skills[i].completed) {
                return; // 已经完成
            }

            // 允许完成任意节点，不检查父节点状态
            bool wasCompleted = tree.skills[i].completed;
            tree.skills[i].completed = true;
            tree.skills[i].unlocked = true; // 完成时自动解锁
            tree.skills[i].progress = 100; // 完成时进度设为100%
            
            QModelIndex idx = index(i, 0);
            emit dataChanged(idx, idx, {CompletedRole, UnlockedRole, ProgressRole});
            
            if (!wasCompleted && tree.skills[i].isLevel) {
                emit levelMedalEarned(m_currentTreeId, tree.skills[i].id, tree.skills[i].name, tree.skills[i].medalIcon);
            }
            
            // 自动保存
            saveData();
            return;
        }
    }
}

// 取消完成技能
void SkillModel::uncompleteSkill(int skillId)
{
    if (m_currentTreeId.isEmpty() || !m_skillTrees.contains(m_currentTreeId)) {
        return;
    }

    SkillTree& tree = currentSkillTree();
    for (int i = 0; i < tree.skills.size(); i++) {
        if (tree.skills[i].id == skillId) {
            if (!tree.skills[i].completed) {
                return; // 本来就未完成
            }

            // 检查是否有子技能已完成（如果有子技能已完成，则不能取消完成）
            bool hasCompletedChildren = false;
            for (const Skill &s : tree.skills) {
                if (s.parent == skillId && s.completed) {
                    hasCompletedChildren = true;
                    break;
                }
            }

            if (!hasCompletedChildren) {
                bool wasCompleted = tree.skills[i].completed;
                tree.skills[i].completed = false;
                tree.skills[i].starRating = 0;  // 重置星级
                // 保持unlocked状态和progress值不变
                
                QModelIndex idx = index(i, 0);
                emit dataChanged(idx, idx, {CompletedRole, StarRatingRole});
                
                if (wasCompleted && tree.skills[i].isLevel) {
                    emit levelMedalRevoked(m_currentTreeId, skillId);
                }
                
                // 标记数据已修改
                markDataAsModified();
                // 自动保存
                saveData();

                updatePlayerStats();
            }
            return;
        }
    }
}

// 带星级完成技能
void SkillModel::completeSkillWithStars(int skillId, int stars)
{
    if (m_currentTreeId.isEmpty() || !m_skillTrees.contains(m_currentTreeId)) {
        return;
    }
    
    // 验证星级有效性（至少1）
    if (stars < 1) {
        qWarning() << "Invalid star rating:" << stars << ". Must be at least 1.";
        return;
    }

    SkillTree& tree = currentSkillTree();
    for (int i = 0; i < tree.skills.size(); i++) {
        if (tree.skills[i].id == skillId) {
            bool wasCompleted = tree.skills[i].completed;
            int maxAllowedStars = tree.skills[i].maxStarRating > 0
                                      ? tree.skills[i].maxStarRating
                                      : (tree.skills[i].isLevel ? 5 : 3);
            maxAllowedStars = qBound(1, maxAllowedStars, 5);
            int clampedStars = qBound(1, stars, maxAllowedStars);
            
            // 更新完成状态和星级
            tree.skills[i].completed = true;
            tree.skills[i].unlocked = true;
            tree.skills[i].progress = 100;
            tree.skills[i].starRating = clampedStars;
            
            QModelIndex idx = index(i, 0);
            emit dataChanged(idx, idx, {CompletedRole, UnlockedRole, ProgressRole, StarRatingRole});
            
            if (!wasCompleted && tree.skills[i].isLevel) {
                emit levelMedalEarned(m_currentTreeId, tree.skills[i].id, tree.skills[i].name, tree.skills[i].medalIcon);
            }
            
            // 标记数据已修改
            markDataAsModified();
            // 自动保存
            saveData();

            updatePlayerStats();
            return;
        }
    }
}

// 获取技能星级
int SkillModel::getSkillStarRating(int skillId)
{
    if (m_currentTreeId.isEmpty() || !m_skillTrees.contains(m_currentTreeId)) {
        return 0;
    }

    const SkillTree& tree = currentSkillTree();
    for (const Skill& skill : tree.skills) {
        if (skill.id == skillId) {
            return skill.starRating;
        }
    }
    return 0;  // 未找到或未完成
}

int SkillModel::getSkillMaxStarRating(int skillId) const
{
    if (m_currentTreeId.isEmpty() || !m_skillTrees.contains(m_currentTreeId)) {
        return 3;
    }

    const SkillTree& tree = currentSkillTree();
    for (const Skill& skill : tree.skills) {
        if (skill.id == skillId) {
            int maxStars = skill.maxStarRating > 0 ? skill.maxStarRating : (skill.isLevel ? 5 : 3);
            return qBound(1, maxStars, 5);
        }
    }
    return 3;
}

QVariantList SkillModel::getAvailableMedalIcons() const
{
    QVariantList icons;
    QSet<QString> unique;
    
    // 默认Emoji勋章
    const QStringList defaultIcons = {
        QStringLiteral("🏅"), QStringLiteral("🎖️"), QStringLiteral("👑"), QStringLiteral("🌟"),
        QStringLiteral("💎"), QStringLiteral("🚀"), QStringLiteral("🛡️"), QStringLiteral("🎯"),
        QStringLiteral("⚡"),
        
        QStringLiteral("🪐"), QStringLiteral("🌍"), QStringLiteral("🌎"), QStringLiteral("🌏"),
        QStringLiteral("☀️"), QStringLiteral("🌙"), QStringLiteral("🌠"), QStringLiteral("🌌"),
        QStringLiteral("🛰️"), QStringLiteral("🛸"), QStringLiteral("🧑‍🚀"), QStringLiteral("🔭"),
        
        QStringLiteral("🐶"), QStringLiteral("🐱"), QStringLiteral("🐭"), QStringLiteral("🐹"),
        QStringLiteral("🐰"), QStringLiteral("🦊"), QStringLiteral("🐻"), QStringLiteral("🐼"),
        QStringLiteral("🐨"), QStringLiteral("🐯"), QStringLiteral("🦁"), QStringLiteral("🐮"),
        QStringLiteral("🐷"), QStringLiteral("🐸"), QStringLiteral("🐵"), QStringLiteral("🦍"),
        QStringLiteral("🦧"), QStringLiteral("🦓"), QStringLiteral("🦒"), QStringLiteral("🦌"),
        QStringLiteral("🦘"), QStringLiteral("🦬"), QStringLiteral("🐺"), QStringLiteral("🦄"),
        QStringLiteral("🦥"), QStringLiteral("🦦"), QStringLiteral("🦨"), QStringLiteral("🦝"),
        QStringLiteral("🦫"), QStringLiteral("🦡"), QStringLiteral("🦔"), QStringLiteral("🦇"),
        QStringLiteral("🐴"), QStringLiteral("🐗"),
        
        QStringLiteral("🐋"), QStringLiteral("🐳"), QStringLiteral("🐬"), QStringLiteral("🐟"),
        QStringLiteral("🐠"), QStringLiteral("🐡"), QStringLiteral("🦈"), QStringLiteral("🐙"),
        QStringLiteral("🦑"), QStringLiteral("🦀"), QStringLiteral("🦞"), QStringLiteral("🦐"),
        QStringLiteral("🐚"),
        
        QStringLiteral("🐓"), QStringLiteral("🐔"), QStringLiteral("🐣"), QStringLiteral("🐤"),
        QStringLiteral("🐥"), QStringLiteral("🦆"), QStringLiteral("🦅"), QStringLiteral("🦉"),
        QStringLiteral("🦢"), QStringLiteral("🦜"), QStringLiteral("🦚"), QStringLiteral("🦩"),
        QStringLiteral("🕊️"), QStringLiteral("🐧"),
        
        QStringLiteral("🦎"), QStringLiteral("🐍"), QStringLiteral("🐢"), QStringLiteral("🦕"),
        QStringLiteral("🦖"),
        
        QStringLiteral("🐝"), QStringLiteral("🐞"), QStringLiteral("🦋"), QStringLiteral("🪲"),
        QStringLiteral("🪳"), QStringLiteral("🦟"), QStringLiteral("🪰"), QStringLiteral("🐌"),
        QStringLiteral("🐛"), QStringLiteral("🪱"),
        
        QStringLiteral("🦸"), QStringLiteral("🦸‍♂️"), QStringLiteral("🦸‍♀️"),
        QStringLiteral("🧒"), QStringLiteral("👦"), QStringLiteral("👧"),
        QStringLiteral("👾"), QStringLiteral("👹"), QStringLiteral("🧌"), QStringLiteral("👻"),
        QStringLiteral("🧟"),
        
        QStringLiteral("🚓"), QStringLiteral("🗺️"), QStringLiteral("🧭")
    };
    for (const QString& icon : defaultIcons) {
        if (!unique.contains(icon)) {
            unique.insert(icon);
            icons.append(icon);
        }
    }
    
    // 资源目录中的图片勋章（位于qrc:/images/medals）
    QDir resourceDir(":/images/medals");
    if (resourceDir.exists()) {
        const QFileInfoList files = resourceDir.entryInfoList(QDir::Files);
        for (const QFileInfo& info : files) {
            const QString resourcePath = QStringLiteral("qrc:/images/medals/%1").arg(info.fileName());
            if (!unique.contains(resourcePath)) {
                unique.insert(resourcePath);
                icons.append(resourcePath);
            }
        }
    }
    
    // 本地自定义目录（AppData/medals）
    QString customDirPath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/medals";
    QDir customDir(customDirPath);
    if (customDir.exists()) {
        const QFileInfoList files = customDir.entryInfoList(QDir::Files);
        for (const QFileInfo& info : files) {
            const QString localPath = QUrl::fromLocalFile(info.absoluteFilePath()).toString();
            if (!unique.contains(localPath)) {
                unique.insert(localPath);
                icons.append(localPath);
            }
        }
    }
    
    if (icons.isEmpty()) {
        icons.append(QStringLiteral("🏅"));
    }
    
    return icons;
}

void SkillModel::refreshPlayerStats()
{
    updatePlayerStats();
}

// QAbstractListModel 接口实现

int SkillModel::rowCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent)
    if (m_currentTreeId.isEmpty() || !m_skillTrees.contains(m_currentTreeId)) {
        return 0;
    }
    const SkillTree &tree = currentSkillTree();
    return tree.skills.size();
}

QVariant SkillModel::data(const QModelIndex &index, int role) const
{
    if (!index.isValid() || index.row() < 0) {
        return QVariant();
    }
    
    const SkillTree &tree = currentSkillTree();
    if (index.row() >= tree.skills.size())
        return QVariant();

    const Skill &skill = tree.skills.at(index.row());
    switch (role) {
    case IdRole: return skill.id;
    case NameRole: return skill.name;
    case ParentRole: return skill.parent;
    case XRole: return skill.x;
    case YRole: return skill.y;
    case UnlockedRole: return skill.unlocked;
    case CompletedRole: return skill.completed;
    case SelectedRole: return skill.selected;
    case CategoryRole: return skill.category;
    case DescriptionRole: return skill.description;
    case DifficultyRole: return skill.difficulty;
    case ProgressRole: return skill.progress;
    case StarRatingRole: return skill.starRating;
    case IsLevelRole: return skill.isLevel;
    case MaxStarRatingRole: return skill.maxStarRating;
    case MedalIconRole: return skill.medalIcon;
    default: return QVariant();
    }
}

QHash<int, QByteArray> SkillModel::roleNames() const
{
    return {
        {IdRole, "id"},
        {NameRole, "name"},
        {ParentRole, "parent"},
        {XRole, "x"},
        {YRole, "y"},
        {UnlockedRole, "unlocked"},
        {CompletedRole, "completed"},
        {SelectedRole, "selected"},
        {CategoryRole, "category"},
        {DescriptionRole, "description"},
        {DifficultyRole, "difficulty"},
        {ProgressRole, "progress"},
        {StarRatingRole, "starRating"},
        {IsLevelRole, "isLevel"},
        {MaxStarRatingRole, "maxStarRating"},
        {MedalIconRole, "medalIcon"}
    };
}

// Current Tree ID 属�?
QString SkillModel::currentTreeId() const
{
    return m_currentTreeId;
}

void SkillModel::setCurrentTreeId(const QString& treeId)
{
    if (m_currentTreeId == treeId)
        return;

    // 修复后的逻辑：无条件地调用 begin/endResetModel，因为切换数据源总是结构性变化
    beginResetModel(); // 以前是 beginResetCurrentModel()
    m_currentTreeId = treeId;
    endResetModel(); // 以前是 endResetCurrentModel()

    emit currentTreeIdChanged();
    emit countChanged();
}

// 辅助函数

SkillTree& SkillModel::currentSkillTree()
{
    // 安全检查：确保m_currentTreeId有效
    if (m_currentTreeId.isEmpty() || !m_skillTrees.contains(m_currentTreeId)) {
        qWarning() << "Warning: currentTreeId is invalid, using first available tree";
        if (!m_skillTrees.isEmpty()) {
            m_currentTreeId = m_skillTrees.firstKey();
        }
    }
    
    // 如果 key 不存在，QMap::operator[] 会插入一个默认构造的 SkillTree
    // 在这里我们依赖前面的检查确保 m_currentTreeId 是有效的
    return m_skillTrees[m_currentTreeId];
}

const SkillTree& SkillModel::currentSkillTree() const
{
    // 使用 value() 更安全地访问 const 对象
    static const SkillTree emptyTree = {QUuid(), QString(), QString(), QList<Skill>(), 0};
    if (m_skillTrees.contains(m_currentTreeId)) {
        // 修复：避免返回临时对象引用，使用find()获取迭代器
        auto it = m_skillTrees.find(m_currentTreeId);
        if (it != m_skillTrees.end()) {
            return it.value();
        }
    }
    return emptyTree;
}

// QML 可调用：更新技能进度
void SkillModel::updateProgress(int skillId, int progress)
{
    
    if (m_currentTreeId.isEmpty() || !m_skillTrees.contains(m_currentTreeId)) {
        return;
    }

    SkillTree& tree = currentSkillTree();
    for (int i = 0; i < tree.skills.size(); i++) {
        if (tree.skills[i].id == skillId) {
            tree.skills[i].progress = qBound(0, progress, 100); // 限制进度在0-100之间
            QModelIndex idx = index(i, 0);
            emit dataChanged(idx, idx, {ProgressRole});
            
            // 标记数据已修改
            markDataAsModified();
            // 自动保存
            saveData();
            
            // 如果进度达到100%，自动解锁技能
            if (tree.skills[i].progress >= 100 && !tree.skills[i].unlocked) {
                unlockSkill(skillId);
            }
            return;
        }
    }
    
}

// QML 可调用：更新技能名称
void SkillModel::updateSkillName(int skillId, QString newName)
{
    
    if (m_currentTreeId.isEmpty() || !m_skillTrees.contains(m_currentTreeId)) {
        return;
    }

    SkillTree& tree = currentSkillTree();
    for (int i = 0; i < tree.skills.size(); i++) {
        if (tree.skills[i].id == skillId) {
            tree.skills[i].name = newName;
            
            QModelIndex idx = index(i, 0);
            emit dataChanged(idx, idx, {NameRole});
            
            // 标记数据已修改
            markDataAsModified();
            // 自动保存
            saveData();
            
            if (tree.skills[i].isLevel && tree.skills[i].completed) {
                emit levelMedalUpdated(m_currentTreeId, skillId, newName, tree.skills[i].medalIcon);
            }
            return;
        }
    }
    
}

// QML 可调用：更新技能树名称
void SkillModel::updateSkillTreeName(QString treeId, QString newName)
{
    
    if (m_skillTrees.contains(treeId)) {
        m_skillTrees[treeId].name = newName;
        
        // 标记数据已修改
        markDataAsModified();
        // 自动保存
        saveData();
    } else {
    }
}

// QML 可调用：选中技能
void SkillModel::selectSkill(int skillId)
{
    
    if (m_currentTreeId.isEmpty() || !m_skillTrees.contains(m_currentTreeId)) {
        return;
    }

    SkillTree& tree = currentSkillTree();
    
    // 先取消所有技能的选中状态
    for (int i = 0; i < tree.skills.size(); i++) {
        if (tree.skills[i].selected) {
            tree.skills[i].selected = false;
            QModelIndex idx = index(i, 0);
            emit dataChanged(idx, idx, {SelectedRole});
        }
    }
    
    // 选中指定技能
    for (int i = 0; i < tree.skills.size(); i++) {
        if (tree.skills[i].id == skillId) {
            tree.skills[i].selected = true;
            QModelIndex idx = index(i, 0);
            emit dataChanged(idx, idx, {SelectedRole});
            return;
        }
    }
    
}

// QML 可调用：添加子技能
void SkillModel::addChildSkill(int parentId, QString name)
{
    
    if (m_currentTreeId.isEmpty() || !m_skillTrees.contains(m_currentTreeId)) {
        return;
    }

    SkillTree& tree = currentSkillTree();
    
    // 找到父技能
    Skill* parentSkill = nullptr;
    for (int i = 0; i < tree.skills.size(); i++) {
        if (tree.skills[i].id == parentId) {
            parentSkill = &tree.skills[i];
            break;
        }
    }
    
    if (!parentSkill) {
        return;
    }
    
    // 添加子技能（位置计算已在addSkill中处理）
    addSkill(name, parentId, parentSkill->category, "子技能", parentSkill->difficulty + 1);
    
}

void SkillModel::addChildLevel(int parentId, QString name, int maxStars, QString medalIcon)
{
    
    if (m_currentTreeId.isEmpty() || !m_skillTrees.contains(m_currentTreeId)) {
        return;
    }

    SkillTree& tree = currentSkillTree();
    
    Skill* parentSkill = nullptr;
    for (int i = 0; i < tree.skills.size(); i++) {
        if (tree.skills[i].id == parentId) {
            parentSkill = &tree.skills[i];
            break;
        }
    }

    if (!parentSkill) {
        return;
    }

    int adjustedDifficulty = std::max(1, parentSkill->difficulty);
    addSkill(name, parentId, parentSkill->category, "关卡节点", adjustedDifficulty, true, maxStars, medalIcon);
    
}

// QML 可调用：删除技能及其所有子技能
void SkillModel::deleteSkill(int skillId)
{
    
    if (m_currentTreeId.isEmpty() || !m_skillTrees.contains(m_currentTreeId)) {
        return;
    }

    SkillTree& tree = currentSkillTree();
    
    // 检查是否是根节点
    bool isRootNode = false;
    for (const Skill& skill : tree.skills) {
        if (skill.id == skillId && skill.parent == -1) {
            isRootNode = true;
            break;
        }
    }
    
    // 如果是根节点，检查是否有子节点
    if (isRootNode) {
        bool hasChildren = false;
        for (const Skill& skill : tree.skills) {
            if (skill.parent == skillId) {
                hasChildren = true;
                break;
            }
        }
        
        if (hasChildren) {
            emit syncFailed("无法删除根节点：该根节点下还有子节点。请先删除所有子节点后再删除根节点。");
            return;
        }
    }
    
    // 递归删除技能及其所有子技能
    QList<int> skillsToDelete;
    collectChildrenSkills(skillId, skillsToDelete);
    skillsToDelete.append(skillId); // 包含自己
    
    
    // 按ID从大到小排序，确保先删除子技能再删除父技能
    std::sort(skillsToDelete.begin(), skillsToDelete.end(), std::greater<int>());
    
    for (int skillIdToDelete : skillsToDelete) {
        for (int i = 0; i < tree.skills.size(); i++) {
            if (tree.skills[i].id == skillIdToDelete) {
                Skill removedSkill = tree.skills[i];
                beginRemoveRows(QModelIndex(), i, i);
                tree.skills.removeAt(i);
                endRemoveRows();
                if (removedSkill.isLevel && removedSkill.completed) {
                    emit levelMedalRevoked(m_currentTreeId, removedSkill.id);
                }
                break;
            }
        }
    }
    
    // 删除后重新排列技能树，使其美观、对称
    rearrangeSkillTree();
    
    // 标记数据已修改
    markDataAsModified();
    // 自动保存
    saveData();
    emit countChanged();
    
}

// 辅助函数：收集所有子技能ID
void SkillModel::collectChildrenSkills(int parentId, QList<int>& children)
{
    if (m_currentTreeId.isEmpty() || !m_skillTrees.contains(m_currentTreeId)) {
        return;
    }
    
    const SkillTree& tree = currentSkillTree();
    for (const Skill& skill : tree.skills) {
        if (skill.parent == parentId) {
            children.append(skill.id);
            collectChildrenSkills(skill.id, children); // 递归收集子技能
        }
    }
}

// 改进的树形布局算法：重新排列整个技能树
void SkillModel::rearrangeSkillTree()
{
    if (m_currentTreeId.isEmpty() || !m_skillTrees.contains(m_currentTreeId)) {
        return;
    }
    
    SkillTree& tree = currentSkillTree();
    if (tree.skills.isEmpty()) {
        return;
    }
    
    // 0. 预处理：验证所有节点的parent关系
    QSet<int> existingSkillIds;
    for (const Skill& skill : tree.skills) {
        existingSkillIds.insert(skill.id);
    }
    
    // 修复：将parent指向不存在节点的技能变为根节点
    for (int i = 0; i < tree.skills.size(); i++) {
        if (tree.skills[i].parent != -1 && !existingSkillIds.contains(tree.skills[i].parent)) {
            tree.skills[i].parent = -1;
        }
    }
    
    // 1. 构建树结构：找出所有根节点和子节点关系
    QMap<int, QList<Skill*>> childrenMap; // parentId -> children
    QList<Skill*> rootNodes;
    
    for (int i = 0; i < tree.skills.size(); i++) {
        if (tree.skills[i].parent == -1) {
            rootNodes.append(&tree.skills[i]);
        } else {
            childrenMap[tree.skills[i].parent].append(&tree.skills[i]);
        }
    }
    
    // 按ID排序以保持稳定性
    for (auto& children : childrenMap) {
        std::sort(children.begin(), children.end(), [](const Skill* a, const Skill* b) {
            return a->id < b->id;
        });
    }
    
    
    // 2. 计算树的层级结构
    QMap<int, int> skillDepth; // skillId -> depth (depth 0 = root)
    QMap<int, QList<Skill*>> levelNodes; // depth -> nodes at this level
    QSet<int> visitedSkills; // 记录已访问的节点，防止循环引用
    QList<QPair<int, int>> circularReferences; // 记录循环引用：(childId, parentId)
    
    // 使用BFS计算每个节点的深度
    QList<QPair<Skill*, int>> queue; // (skill, depth)
    for (Skill* root : rootNodes) {
        queue.append(qMakePair(root, 0));
        visitedSkills.insert(root->id);
    }
    
    int maxDepth = 0;
    while (!queue.isEmpty()) {
        auto pair = queue.takeFirst();
        Skill* skill = pair.first;
        int depth = pair.second;
        
        skillDepth[skill->id] = depth;
        levelNodes[depth].append(skill);
        maxDepth = std::max(maxDepth, depth);
        
        // 添加子节点到队列（只添加未访问过的节点）
        if (childrenMap.contains(skill->id)) {
            for (Skill* child : childrenMap[skill->id]) {
                if (!visitedSkills.contains(child->id)) {
                    queue.append(qMakePair(child, depth + 1));
                    visitedSkills.insert(child->id);
                } else {
                    // 检测到循环引用
                    circularReferences.append(qMakePair(child->id, skill->id));
                }
            }
        }
    }
    
    // 检查是否有节点未被访问到（可能是孤立的子树）
    QSet<int> allSkillIds;
    for (const Skill& skill : tree.skills) {
        allSkillIds.insert(skill.id);
    }
    QSet<int> unvisitedSkills = allSkillIds - visitedSkills;
    if (!unvisitedSkills.isEmpty()) {
        for (int skillId : unvisitedSkills) {
            for (Skill& skill : tree.skills) {
                if (skill.id == skillId) {
                    // 将未访问的节点修正为根节点
                    skill.parent = -1;
                    break;
                }
            }
        }
        // 如果修正了节点，需要重新构建树结构
        if (!unvisitedSkills.isEmpty()) {
            // 递归调用自己重新处理
            rearrangeSkillTree();
            return;
        }
    }
    
    
    // 3. 自底向上计算每个节点的宽度和中心位置
    const int nodeWidth = 60; // 节点间距（原值125的约一半）
    const int groupSpacing = 10; // 不同父节点组之间的额外间距（原值20的一半）
    
    QMap<int, int> subtreeWidth; // skillId -> subtree width
    QMap<int, double> relativeX; // skillId -> relative X position within parent's subtree
    
    // 从最底层开始向上计算
    for (int depth = maxDepth; depth >= 0; depth--) {
        for (Skill* skill : levelNodes[depth]) {
            if (!childrenMap.contains(skill->id) || childrenMap[skill->id].isEmpty()) {
                // 叶子节点：宽度为1个单位
                subtreeWidth[skill->id] = nodeWidth;
                relativeX[skill->id] = nodeWidth / 2.0; // 叶子节点居中
            } else {
                // 非叶子节点：宽度为所有子节点的总宽度
                QList<Skill*>& children = childrenMap[skill->id];
                int totalWidth = 0;
                for (Skill* child : children) {
                    totalWidth += subtreeWidth[child->id];
                }
                // 添加子节点之间的间距
                totalWidth += (children.size() - 1) * groupSpacing;
                
                subtreeWidth[skill->id] = totalWidth;
                
                // 父节点位置：子节点的中心
                int firstChildWidth = subtreeWidth[children.first()->id];
                int lastChildWidth = subtreeWidth[children.last()->id];
                relativeX[skill->id] = (firstChildWidth / 2.0 + lastChildWidth / 2.0 + 
                                       (totalWidth - firstChildWidth - lastChildWidth) / 2.0);
            }
        }
    }
    
    // 4. 计算所有根节点的总宽度
    int totalTreeWidth = 0;
    for (Skill* root : rootNodes) {
        totalTreeWidth += subtreeWidth[root->id];
    }
    totalTreeWidth += (rootNodes.size() - 1) * groupSpacing * 2; // 根节点之间的额外间距
    
    // 5. 自顶向下分配实际坐标
    const int startX = 1000; // 树的起始X坐标（屏幕中心）
    const int levelHeight = 70; // 层级之间的垂直间距（原值150的约一半）
    const int rootY = 2000; // 根节点的Y坐标（底部）
    
    QMap<int, int> absoluteX; // skillId -> absolute X position
    
    // 计算根节点的起始X坐标（居中）
    int currentRootX = startX - totalTreeWidth / 2;
    
    for (Skill* root : rootNodes) {
        // 分配根节点的X坐标
        absoluteX[root->id] = currentRootX + subtreeWidth[root->id] / 2;
        root->x = absoluteX[root->id] - 30; // 减去节点半径，确保中心对齐
        root->y = rootY;
        
        // 递归分配子节点的坐标
        arrangeChildren(root, childrenMap, subtreeWidth, currentRootX, 
                       rootY - levelHeight, levelHeight, groupSpacing, absoluteX);
        
        currentRootX += subtreeWidth[root->id] + groupSpacing * 2;
    }
    
    // 6. 通知UI更新
    emit dataChanged(index(0), index(rowCount() - 1), {XRole, YRole});
    
}

// 辅助函数：递归排列子节点
void SkillModel::arrangeChildren(Skill* parent, 
                                 QMap<int, QList<Skill*>>& childrenMap,
                                 QMap<int, int>& subtreeWidth,
                                 int startX,
                                 int currentY,
                                 int levelHeight,
                                 int groupSpacing,
                                 QMap<int, int>& absoluteX)
{
    // 使用静态变量跟踪递归深度和访问节点
    static int recursionDepth = 0;
    static QSet<int> processingNodes;
    
    recursionDepth++;
    
    // 防止递归过深
    if (recursionDepth > 100) {
        recursionDepth--;
        return;
    }
    
    // 防止循环引用导致的无限递归
    if (processingNodes.contains(parent->id)) {
        recursionDepth--;
        return;
    }
    
    if (!childrenMap.contains(parent->id)) {
        recursionDepth--;
        return;
    }
    
    QList<Skill*>& children = childrenMap[parent->id];
    if (children.isEmpty()) {
        recursionDepth--;
        return;
    }
    
    processingNodes.insert(parent->id);
    
    // 计算子节点的起始X坐标（在父节点下方居中）
    int totalChildrenWidth = 0;
    for (Skill* child : children) {
        if (subtreeWidth.contains(child->id)) {
            totalChildrenWidth += subtreeWidth[child->id];
        }
    }
    totalChildrenWidth += (children.size() - 1) * groupSpacing;
    
    int childStartX = absoluteX[parent->id] - totalChildrenWidth / 2;
    
    int currentChildX = childStartX;
    for (Skill* child : children) {
        // 分配子节点的X坐标
        absoluteX[child->id] = currentChildX + subtreeWidth[child->id] / 2;
        child->x = absoluteX[child->id] - 30; // 减去节点半径
        child->y = currentY;
        
        
        // 递归处理子节点的子节点
        arrangeChildren(child, childrenMap, subtreeWidth, currentChildX, 
                       currentY - levelHeight, levelHeight, groupSpacing, absoluteX);
        
        currentChildX += subtreeWidth[child->id] + groupSpacing;
    }
    
    processingNodes.remove(parent->id);
    recursionDepth--;
    
    // 清理静态变量
    if (recursionDepth == 0) {
        processingNodes.clear();
    }
}

// 重新排列同层级的节点，按父节点分组（保留旧方法作为备用）
void SkillModel::rearrangeChildrenInLayer(int yLevel)
{
    if (m_currentTreeId.isEmpty() || !m_skillTrees.contains(m_currentTreeId)) {
        return;
    }
    
    SkillTree& tree = currentSkillTree();
    
    // 找出该层级的所有节点
    QList<Skill*> nodesInLayer;
    for (int i = 0; i < tree.skills.size(); i++) {
        if (std::abs(tree.skills[i].y - yLevel) < 50) {
            nodesInLayer.append(&tree.skills[i]);
        }
    }
    
    if (nodesInLayer.isEmpty()) {
        return;
    }
    
    
    // 按父节点ID分组
    QMap<int, QList<Skill*>> groupedByParent;
    for (Skill* skill : nodesInLayer) {
        groupedByParent[skill->parent].append(skill);
    }
    
    // 获取所有父节点并按X坐标排序（从左到右）
    QList<int> parentIds = groupedByParent.keys();
    std::sort(parentIds.begin(), parentIds.end(), [&tree](int parentId1, int parentId2) {
        // 找到父节点的X坐标
        int x1 = -1, x2 = -1;
        for (const Skill& skill : tree.skills) {
            if (skill.id == parentId1) x1 = skill.x;
            if (skill.id == parentId2) x2 = skill.x;
        }
        return x1 < x2; // 按父节点的X坐标从小到大排序
    });
    
    
    const int nodeSpacing = 60; // 同组内节点间距（原值125的约一半）
    const int groupSpacing = 10; // 不同组之间的额外间距（原值20的一半）
    
    // 第一步：计算总宽度
    int totalWidth = 0;
    for (int parentId : parentIds) {
        QList<Skill*>& group = groupedByParent[parentId];
        totalWidth += group.size() * nodeSpacing; // 组内节点占用的宽度
        totalWidth += groupSpacing; // 组间间距
    }
    totalWidth -= groupSpacing; // 最后一组不需要额外间距
    
    // 第二步：计算起始X坐标（居中对齐）
    // 找出父节点的平均X位置作为居中参考
    int parentXSum = 0;
    int parentCount = 0;
    for (int parentId : parentIds) {
        for (const Skill& skill : tree.skills) {
            if (skill.id == parentId) {
                parentXSum += skill.x;
                parentCount++;
                break;
            }
        }
    }
    
    int centerX = parentCount > 0 ? parentXSum / parentCount : 1000;
    int startX = centerX - totalWidth / 2; // 居中起始位置
    
    // 确保起始位置不会太靠左
    startX = std::max(startX, 200);
    
    
    // 第三步：按父节点的X坐标顺序遍历每个组，分配X坐标
    int currentX = startX;
    for (int parentId : parentIds) {
        QList<Skill*>& group = groupedByParent[parentId];
        
        
        // 对同组内的节点按ID排序（保持稳定顺序）
        std::sort(group.begin(), group.end(), [](const Skill* a, const Skill* b) {
            return a->id < b->id;
        });
        
        // 分配X坐标
        for (Skill* skill : group) {
            int oldX = skill->x;
            skill->x = currentX;
            
            
            currentX += nodeSpacing;
        }
        
        // 组之间额外间距
        currentX += groupSpacing;
    }
    
    // 通知UI更新
    emit dataChanged(index(0), index(rowCount() - 1), {XRole});
    
}

// 保存数据到本地
void SkillModel::saveData()
{
    QString dataPath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
    QDir().mkpath(dataPath);
    
    QJsonObject rootObject;
    QJsonArray skillTreesArray;
    
    for (auto it = m_skillTrees.begin(); it != m_skillTrees.end(); ++it) {
        const SkillTree& tree = it.value();
        QJsonObject treeObject;
        treeObject["id"] = tree.id.toString();
        treeObject["name"] = tree.name;
        treeObject["category"] = tree.category;
        treeObject["nextId"] = tree.nextId;
        
        QJsonArray skillsArray;
        for (const Skill& skill : tree.skills) {
            QJsonObject skillObject;
            skillObject["id"] = skill.id;
            skillObject["name"] = skill.name;
            skillObject["parent"] = skill.parent;
            skillObject["x"] = skill.x;
            skillObject["y"] = skill.y;
            skillObject["unlocked"] = skill.unlocked;
            skillObject["completed"] = skill.completed;
            // 不保存 selected，因为它是UI状态
            skillObject["category"] = skill.category;
            skillObject["description"] = skill.description;
            skillObject["difficulty"] = skill.difficulty;
            skillObject["progress"] = skill.progress;
            skillObject["starRating"] = skill.starRating;
            skillObject["isLevel"] = skill.isLevel;
            skillObject["maxStarRating"] = skill.maxStarRating;
            skillsArray.append(skillObject);
        }
        treeObject["skills"] = skillsArray;
        skillTreesArray.append(treeObject);
    }
    
    rootObject["skillTrees"] = skillTreesArray;
    rootObject["currentTreeId"] = m_currentTreeId;
    
    QJsonDocument doc(rootObject);
    QString filePath = dataPath + "/skill_trees.json";
    
    QFile file(filePath);
    if (file.open(QIODevice::WriteOnly)) {
        file.write(doc.toJson());
        file.close();
        
        // 注意：不再自动上传到云端
        // 只在用户退出界面时检查并弹窗确认是否上传
    }
}

// 从本地加载数据
void SkillModel::loadData()
{
    QString dataPath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
    QString filePath = dataPath + "/skill_trees.json";
    
    QFile file(filePath);
    if (!file.exists()) {
        return;
    }
    
    if (!file.open(QIODevice::ReadOnly)) {
        return;
    }
    
    QByteArray data = file.readAll();
    file.close();
    
    QJsonParseError error;
    QJsonDocument doc = QJsonDocument::fromJson(data, &error);
    
    if (error.error != QJsonParseError::NoError) {
        return;
    }
    
    QJsonObject rootObject = doc.object();
    QJsonArray skillTreesArray = rootObject["skillTrees"].toArray();
    
    m_skillTrees.clear();
    
    for (const QJsonValue& value : skillTreesArray) {
        QJsonObject treeObject = value.toObject();
        SkillTree tree;
        tree.id = QUuid(treeObject["id"].toString());
        tree.name = treeObject["name"].toString();
        tree.category = treeObject["category"].toString();
        tree.nextId = treeObject["nextId"].toInt();
        
        QJsonArray skillsArray = treeObject["skills"].toArray();
        for (const QJsonValue& skillValue : skillsArray) {
            QJsonObject skillObject = skillValue.toObject();
            Skill skill;
            skill.id = skillObject["id"].toInt();
            skill.name = skillObject["name"].toString();
            skill.parent = skillObject["parent"].toInt();
            skill.x = skillObject["x"].toInt();
            skill.y = skillObject["y"].toInt();
            skill.unlocked = skillObject["unlocked"].toBool();
            skill.completed = skillObject["completed"].toBool();
            skill.selected = false;  // 始终默认为未选中（UI状态不持久化）
            skill.category = skillObject["category"].toString();
            skill.description = skillObject["description"].toString();
            skill.difficulty = skillObject["difficulty"].toInt();
            skill.progress = skillObject["progress"].toInt();
            skill.starRating = skillObject["starRating"].toInt(0);  // 默认值为0（未完成或旧数据）
            skill.isLevel = skillObject["isLevel"].toBool(false);
            int defaultMaxStars = skill.isLevel ? 5 : 3;
            skill.maxStarRating = skillObject.contains("maxStarRating")
                                      ? skillObject["maxStarRating"].toInt(defaultMaxStars)
                                      : defaultMaxStars;
            skill.maxStarRating = qBound(1, skill.maxStarRating, 5);
            skill.medalIcon = skillObject["medalIcon"].toString();
            if (skill.isLevel && skill.medalIcon.trimmed().isEmpty()) {
                skill.medalIcon = QStringLiteral("🏅");
            }
            tree.skills.append(skill);
        }
        
        QString treeIdString = tree.id.toString(QUuid::WithoutBraces);
        m_skillTrees[treeIdString] = tree;
    }
    
    m_currentTreeId = rootObject["currentTreeId"].toString();

    updatePlayerStats();
}

QString SkillModel::getDataFilePath() const
{
    QString dataPath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
    return dataPath + "/skill_trees.json";
}

bool SkillModel::exportSkillTrees(const QString& destinationPath)
{
    QString dest = destinationPath;
    if (dest.trimmed().isEmpty()) {
        return false;
    }

    saveData();

    QString src = getDataFilePath();

    QUrl url(dest);
    if (url.isValid() && url.scheme().startsWith("file")) {
        QString local = url.toLocalFile();
        if (!local.isEmpty()) {
            dest = local;
        }
    }

    QFileInfo info(dest);
    if (info.isDir()) {
        QString base = info.absoluteFilePath();
        if (!base.endsWith('/')) {
            base += '/';
        }
        dest = base + "skill_trees.json";
        info = QFileInfo(dest);
    }

    QDir dir;
    dir.mkpath(info.dir().absolutePath());

    if (QFile::exists(dest)) {
        QFile::remove(dest);
    }

    bool ok = QFile::copy(src, dest);
    return ok;
}

bool SkillModel::importSkillTrees(const QString& sourcePath)
{
    QString src = sourcePath;
    if (src.trimmed().isEmpty()) {
        return false;
    }

    QUrl url(src);
    if (url.isValid() && url.scheme().startsWith("file")) {
        QString local = url.toLocalFile();
        if (!local.isEmpty()) {
            src = local;
        }
    }

    QFile srcFile(src);
    if (!srcFile.exists()) {
        emit syncFailed(QStringLiteral("导入失败：源文件不存在"));
        return false;
    }

    if (!srcFile.open(QIODevice::ReadOnly)) {
        emit syncFailed(QStringLiteral("导入失败：无法读取源文件"));
        return false;
    }

    QByteArray data = srcFile.readAll();
    srcFile.close();

    QJsonParseError parseError;
    QJsonDocument doc = QJsonDocument::fromJson(data, &parseError);
    if (parseError.error != QJsonParseError::NoError || !doc.isObject()) {
        emit syncFailed(QStringLiteral("导入失败：JSON格式无效"));
        return false;
    }

    QString dest = getDataFilePath();
    QFile destFile(dest);
    QDir().mkpath(QFileInfo(dest).dir().absolutePath());

    if (!destFile.open(QIODevice::WriteOnly)) {
        emit syncFailed(QStringLiteral("导入失败：无法写入目标文件"));
        return false;
    }

    destFile.write(doc.toJson());
    destFile.close();

    loadData();
    emit countChanged();
    markDataAsModified();
    saveData();
    return true;
}

// QML 可调用：获取指定索引的技能数据
QVariantMap SkillModel::get(int index) const
{
    QVariantMap map;
    if (m_currentTreeId.isEmpty() || !m_skillTrees.contains(m_currentTreeId)) {
        return map;
    }
    
    // 修复：直接使用QMap的find()避免临时对象引用问题
    auto it = m_skillTrees.find(m_currentTreeId);
    if (it != m_skillTrees.end()) {
        const SkillTree &tree = it.value();
        if (index >= 0 && index < tree.skills.size()) {
            const Skill &skill = tree.skills.at(index);
            map["id"] = skill.id;
            map["name"] = skill.name;
            map["parent"] = skill.parent;
            map["x"] = skill.x;
            map["y"] = skill.y;
            map["unlocked"] = skill.unlocked;
            map["completed"] = skill.completed;
            map["category"] = skill.category;
            map["description"] = skill.description;
            map["difficulty"] = skill.difficulty;
            map["progress"] = skill.progress;
            map["starRating"] = skill.starRating;
            map["isLevel"] = skill.isLevel;
            map["maxStarRating"] = skill.maxStarRating;
            map["medalIcon"] = skill.medalIcon;
        }
    }
    return map;
}

// QML 可调用：获取技能数量
int SkillModel::count() const
{
    if (m_currentTreeId.isEmpty() || !m_skillTrees.contains(m_currentTreeId)) {
        return 0;
    }
    
    // 修复：直接使用QMap的find()避免临时对象引用问题
    auto it = m_skillTrees.find(m_currentTreeId);
    if (it != m_skillTrees.end()) {
        return it.value().skills.size();
    }
    
    return 0;
}

// 辅助函数现在只是简单地调用基类方法，以保持代码结构一致
void SkillModel::beginResetCurrentModel() {
    // 修复：移除条件判断，确保配对
    beginResetModel();
}

void SkillModel::endResetCurrentModel() {
    // 修复：移除条件判断，确保配对
    endResetModel();
}

// ==================== 云同步功能实现 ====================

// 设置云同步配置
void SkillModel::setCloudSyncConfig(const QString& owner, const QString& repo, const QString& token)
{
    m_giteeOwner = owner.trimmed();
    
    // 处理仓库名：如果用户输入了完整URL，自动提取仓库名
    QString repoName = repo.trimmed();
    if (repoName.startsWith("http://") || repoName.startsWith("https://")) {
        // 从URL中提取仓库名
        // 例如：https://gitee.com/zuoo/LitterPutting-Data -> LitterPutting-Data
        QStringList parts = repoName.split('/');
        if (parts.size() >= 2) {
            repoName = parts.last();  // 取最后一部分作为仓库名
        }
        qDebug() << "检测到完整URL，自动提取仓库名:" << repoName;
    }
    m_giteeRepo = repoName;
    
    m_giteeToken = token.trimmed();
    
    // 验证配置有效性
    bool oldValid = m_cloudSyncValid;
    m_cloudSyncValid = validateCloudSyncConfig();
    m_autoSyncEnabled = m_cloudSyncValid;
    
    if (oldValid != m_cloudSyncValid) {
        emit cloudSyncValidChanged();
    }
    
    if (!m_cloudSyncValid) {
        qWarning() << "云同步配置验证失败，已禁用所有云同步操作";
        qWarning() << "Owner:" << m_giteeOwner << "Repo:" << m_giteeRepo << "Token长度:" << m_giteeToken.length();
        return;
    }
    
    // 加载保存的SHA值
    QString dataPath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
    QSettings shaSettings(dataPath + "/cloud_sync_config.ini", QSettings::IniFormat);
    m_currentFileSha = shaSettings.value("cloud/current_file_sha", "").toString();
    if (!m_currentFileSha.isEmpty()) {
        qDebug() << "从本地配置加载SHA:" << m_currentFileSha;
    }
    
    qDebug() << "云同步配置已更新并验证通过:" << m_giteeOwner << "/" << m_giteeRepo;
    qDebug() << "API URL:" << getCloudApiUrl();
    
    // 配置完成后检查云端数据（不自动下载）
    if (m_autoSyncEnabled) {
        checkAndSyncOnStartup();
    }
}

// 检查云同步是否已配置
bool SkillModel::isCloudSyncConfigured() const
{
    return !m_giteeOwner.isEmpty() && !m_giteeRepo.isEmpty() && !m_giteeToken.isEmpty();
}

// 验证云同步配置的有效性
bool SkillModel::validateCloudSyncConfig() const
{
    // 基本检查：不能为空
    if (m_giteeOwner.isEmpty() || m_giteeRepo.isEmpty() || m_giteeToken.isEmpty()) {
        qWarning() << "云同步配置验证失败：配置项为空";
        return false;
    }
    
    // Owner 格式检查：只允许字母、数字、下划线、连字符
    QRegularExpression ownerPattern("^[a-zA-Z0-9_-]+$");
    if (!ownerPattern.match(m_giteeOwner).hasMatch()) {
        qWarning() << "云同步配置验证失败：Owner格式无效" << m_giteeOwner;
        return false;
    }
    
    // Repo 格式检查：只允许字母、数字、下划线、连字符、点
    QRegularExpression repoPattern("^[a-zA-Z0-9_.-]+$");
    if (!repoPattern.match(m_giteeRepo).hasMatch()) {
        qWarning() << "云同步配置验证失败：Repo格式无效" << m_giteeRepo;
        return false;
    }
    
    // Token 格式检查：Gitee token 通常是20-40个字符的字符串
    // 允许字母、数字、下划线、连字符
    if (m_giteeToken.length() < 10 || m_giteeToken.length() > 100) {
        qWarning() << "云同步配置验证失败：Token长度无效" << m_giteeToken.length();
        return false;
    }
    
    QRegularExpression tokenPattern("^[a-zA-Z0-9_-]+$");
    if (!tokenPattern.match(m_giteeToken).hasMatch()) {
        qWarning() << "云同步配置验证失败：Token格式无效";
        return false;
    }
    
    return true;
}

// 获取Gitee API URL
QString SkillModel::getCloudApiUrl() const
{
    // Gitee API: https://gitee.com/api/v5/repos/{owner}/{repo}/contents/{path}
    return QString("https://gitee.com/api/v5/repos/%1/%2/contents/skill_trees.json")
        .arg(m_giteeOwner)
        .arg(m_giteeRepo);
}

// 验证仓库是否存在
void SkillModel::validateRepository()
{
    // 访问仓库信息API来验证仓库是否存在
    QString url = QString("https://gitee.com/api/v5/repos/%1/%2?access_token=%3")
        .arg(m_giteeOwner)
        .arg(m_giteeRepo)
        .arg(m_giteeToken);
    
    QNetworkRequest request;
    request.setUrl(QUrl(url));
    request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
    
    QNetworkReply* reply = m_networkManager->get(request);
    
    connect(reply, &QNetworkReply::finished, this, [this, reply]() {
        handleRepositoryValidationReply(reply);
        reply->deleteLater();
    });
}

// 处理仓库验证响应
void SkillModel::handleRepositoryValidationReply(QNetworkReply* reply)
{
    int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
    
    if (reply->error() != QNetworkReply::NoError || statusCode == 404) {
        // 仓库不存在或token无效，配置无效
        qWarning() << "仓库验证失败：仓库不存在或token无效";
        qWarning() << "错误信息:" << reply->errorString();
        qWarning() << "HTTP状态码:" << statusCode;
        
        // 禁用云同步
        bool oldValid = m_cloudSyncValid;
        m_cloudSyncValid = false;
        m_autoSyncEnabled = false;
        
        if (oldValid != m_cloudSyncValid) {
            emit cloudSyncValidChanged();
        }
        
        emit syncFailed("云同步配置无效：仓库不存在或token无效，请检查配置");
        return;
    }
    
    // 仓库存在，只是文件不存在，提示用户上传
    if (statusCode == 200) {
        qDebug() << "仓库验证成功，仓库存在，但文件不存在";
        emit syncFailed("云端文件不存在，是否要上传本地数据到云端？");
    }
}

// 从云端下载数据
void SkillModel::syncFromCloud()
{
    if (!m_cloudSyncValid) {
        emit syncFailed("云同步配置无效，请检查配置");
        return;
    }
    
    if (!isCloudSyncConfigured()) {
        emit syncFailed("云同步未配置");
        return;
    }
    
    emit syncStarted();
    qDebug() << "开始从云端下载技能树数据...";
    
    QString url = getCloudApiUrl() + "?access_token=" + m_giteeToken;
    QNetworkRequest request;
    request.setUrl(QUrl(url));
    request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
    
    QNetworkReply* reply = m_networkManager->get(request);
    
    // 连接回调
    connect(reply, &QNetworkReply::finished, this, [this, reply]() {
        handleDownloadReply(reply);
        reply->deleteLater();
    });
}

// 处理下载响应
void SkillModel::handleDownloadReply(QNetworkReply* reply)
{
    if (reply->error() != QNetworkReply::NoError) {
        QString errorMsg = reply->errorString();
        qDebug() << "从云端下载失败:" << errorMsg;
        
        // 不再自动上传，只返回错误
        if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 404) {
            emit syncFailed("云端文件不存在");
        } else {
            emit syncFailed("下载失败: " + errorMsg);
        }
        return;
    }
    
    // 解析响应
    QByteArray responseData = reply->readAll();
    QJsonParseError parseError;
    QJsonDocument doc = QJsonDocument::fromJson(responseData, &parseError);
    
    if (parseError.error != QJsonParseError::NoError) {
        qDebug() << "解析响应JSON失败:" << parseError.errorString();
        emit syncFailed("解析响应失败");
        return;
    }
    
    QJsonObject obj = doc.object();
    
    // 获取文件的sha值（用于后续更新）
    m_currentFileSha = obj["sha"].toString();
    qDebug() << "获取到云端文件SHA:" << m_currentFileSha;
    
    // 持久化SHA值到本地
    QString dataPath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
    QSettings shaSettings(dataPath + "/cloud_sync_config.ini", QSettings::IniFormat);
    shaSettings.setValue("cloud/current_file_sha", m_currentFileSha);
    shaSettings.sync();
    
    // 获取Base64编码的内容
    QString contentBase64 = obj["content"].toString();
    contentBase64.remove('\n').remove('\r').remove(' '); // 移除空白字符
    
    // Base64解码
    QByteArray decodedData = QByteArray::fromBase64(contentBase64.toUtf8());
    
    // 解析技能树JSON
    QJsonDocument skillTreeDoc = QJsonDocument::fromJson(decodedData, &parseError);
    if (parseError.error != QJsonParseError::NoError) {
        qDebug() << "解析技能树JSON失败:" << parseError.errorString();
        emit syncFailed("解析技能树数据失败");
        return;
    }
    
    // 加载下载的数据
    QJsonObject rootObject = skillTreeDoc.object();
    QJsonArray treesArray = rootObject["skillTrees"].toArray();
    
    // 数据验证：如果云端数据为空，返回错误
    if (treesArray.isEmpty()) {
        qDebug() << "云端数据为空，无法下载";
        emit syncFailed("云端数据为空");
        return;
    }
    
    // 清空现有数据
    beginResetCurrentModel();
    m_skillTrees.clear();
    
    for (const QJsonValue& treeValue : treesArray) {
        QJsonObject treeObj = treeValue.toObject();
        
        SkillTree tree;
        tree.id = QUuid(treeObj["id"].toString());
        tree.name = treeObj["name"].toString();
        tree.category = treeObj["category"].toString();
        tree.nextId = treeObj["nextId"].toInt();  // 读取nextId
        
        QJsonArray skillsArray = treeObj["skills"].toArray();
        for (const QJsonValue& skillValue : skillsArray) {
            QJsonObject skillObj = skillValue.toObject();
            Skill skill;
            skill.id = skillObj["id"].toInt();
            skill.name = skillObj["name"].toString();
            skill.parent = skillObj["parent"].toInt();
            skill.x = skillObj["x"].toDouble();
            skill.y = skillObj["y"].toDouble();
            skill.unlocked = skillObj["unlocked"].toBool();
            skill.completed = skillObj["completed"].toBool();
            skill.selected = false;  // 默认未选中
            skill.category = skillObj["category"].toString();
            skill.description = skillObj["description"].toString();
            skill.difficulty = skillObj["difficulty"].toInt();
            skill.progress = skillObj["progress"].toInt();
            skill.starRating = skillObj["starRating"].toInt();
            skill.isLevel = skillObj["isLevel"].toBool(false);
            int defaultMaxStars = skill.isLevel ? 5 : 3;
            skill.maxStarRating = skillObj.contains("maxStarRating")
                                      ? skillObj["maxStarRating"].toInt(defaultMaxStars)
                                      : defaultMaxStars;
            skill.maxStarRating = qBound(1, skill.maxStarRating, 5);
            skill.medalIcon = skillObj["medalIcon"].toString();
            if (skill.isLevel && skill.medalIcon.trimmed().isEmpty()) {
                skill.medalIcon = QStringLiteral("🏅");
            }
            
            tree.skills.append(skill);
        }
        
        m_skillTrees[tree.id.toString()] = tree;
    }
    
    m_currentTreeId = rootObject["currentTreeId"].toString();
    
    endResetCurrentModel();
    
    // 保存到本地
    saveData();
    
    // 验证：计算下载后的哈希
    QString hashAfterDownload = calculateDataHash();
    qDebug() << "下载后本地哈希:" << hashAfterDownload.left(16) << "...";
    
    // 清除修改标志
    clearModifiedFlag();
    
    qDebug() << "从云端下载成功，已加载" << m_skillTrees.size() << "个技能树";
    emit syncSuccess("✅ 从云端下载成功");
}

// 上传数据到云端
void SkillModel::syncToCloud()
{
    if (!m_cloudSyncValid) {
        emit syncFailed("云同步配置无效，请检查配置");
        return;
    }
    
    if (!isCloudSyncConfigured()) {
        emit syncFailed("云同步未配置");
        return;
    }
    
    emit syncStarted();
    qDebug() << "开始上传技能树数据到云端...";
    
    // 构建JSON数据
    QJsonObject rootObject;
    rootObject["currentTreeId"] = m_currentTreeId;
    
    QJsonArray treesArray;
    for (auto it = m_skillTrees.begin(); it != m_skillTrees.end(); ++it) {
        const SkillTree& tree = it.value();
        
        QJsonObject treeObj;
        treeObj["id"] = tree.id.toString();
        treeObj["name"] = tree.name;
        treeObj["category"] = tree.category;
        treeObj["nextId"] = tree.nextId;  // 必须包含nextId
        
        QJsonArray skillsArray;
        for (const Skill& skill : tree.skills) {
            QJsonObject skillObj;
            skillObj["id"] = skill.id;
            skillObj["name"] = skill.name;
            skillObj["parent"] = skill.parent;
            skillObj["x"] = skill.x;
            skillObj["y"] = skill.y;
            skillObj["unlocked"] = skill.unlocked;
            skillObj["completed"] = skill.completed;
            skillObj["category"] = skill.category;
            skillObj["description"] = skill.description;
            skillObj["difficulty"] = skill.difficulty;
            skillObj["progress"] = skill.progress;
            skillObj["starRating"] = skill.starRating;
            skillObj["isLevel"] = skill.isLevel;
            skillObj["maxStarRating"] = skill.maxStarRating;
            skillObj["medalIcon"] = skill.medalIcon;
            
            skillsArray.append(skillObj);
        }
        
        treeObj["skills"] = skillsArray;
        treesArray.append(treeObj);
    }
    
    rootObject["skillTrees"] = treesArray;
    
    QJsonDocument doc(rootObject);
    QByteArray jsonData = doc.toJson(QJsonDocument::Compact);
    
    // Base64编码
    QString contentBase64 = jsonData.toBase64();
    
    // 构建Gitee API请求
    QJsonObject requestObj;
    requestObj["access_token"] = m_giteeToken;
    requestObj["content"] = contentBase64;
    requestObj["message"] = "Update skill trees from app";
    
    // 如果有sha值，说明是更新操作；否则是创建操作
    bool isUpdate = !m_currentFileSha.isEmpty();
    if (isUpdate) {
        requestObj["sha"] = m_currentFileSha;
    }
    
    QJsonDocument requestDoc(requestObj);
    QByteArray requestData = requestDoc.toJson();
    
    // 构建URL和请求
    QString url = getCloudApiUrl();
    QNetworkRequest request;
    request.setUrl(QUrl(url));
    request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
    
    // 根据是否有sha决定使用POST(创建)还是PUT(更新)
    QNetworkReply* reply;
    if (isUpdate) {
        qDebug() << "========== 更新云端文件 ==========";
        qDebug() << "URL:" << url;
        qDebug() << "SHA:" << m_currentFileSha;
        qDebug() << "数据大小:" << requestData.size() << "bytes";
        reply = m_networkManager->put(request, requestData);
    } else {
        qDebug() << "========== 创建云端文件 ==========";
        qDebug() << "URL:" << url;
        qDebug() << "数据大小:" << requestData.size() << "bytes";
        reply = m_networkManager->post(request, requestData);
    }
    
    // 连接回调
    connect(reply, &QNetworkReply::finished, this, [this, reply]() {
        qDebug() << "上传请求完成";
        handleUploadReply(reply);
        reply->deleteLater();
    });
}

// 处理上传响应
void SkillModel::handleUploadReply(QNetworkReply* reply)
{
    qDebug() << "========== 处理上传响应 ==========";
    qDebug() << "HTTP状态码:" << reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
    
    if (reply->error() != QNetworkReply::NoError) {
        QString errorMsg = reply->errorString();
        QByteArray responseData = reply->readAll();
        qDebug() << "❌ 上传到云端失败:" << errorMsg;
        qDebug() << "响应内容:" << QString::fromUtf8(responseData);
        emit syncFailed("上传失败: " + errorMsg);
        return;
    }
    
    // 解析响应，更新sha值
    QByteArray responseData = reply->readAll();
    qDebug() << "响应数据大小:" << responseData.size() << "bytes";
    
    QJsonParseError parseError;
    QJsonDocument doc = QJsonDocument::fromJson(responseData, &parseError);
    
    if (parseError.error != QJsonParseError::NoError) {
        qDebug() << "❌ 解析响应JSON失败:" << parseError.errorString();
        qDebug() << "响应内容:" << QString::fromUtf8(responseData.left(500));
        emit syncFailed("解析响应失败");
        return;
    }
    
    QJsonObject obj = doc.object();
    QJsonObject contentObj = obj["content"].toObject();
    QString newSha = contentObj["sha"].toString();
    
    if (newSha.isEmpty()) {
        qDebug() << "⚠️ 响应中没有SHA值";
        qDebug() << "完整响应:" << QString::fromUtf8(responseData.left(500));
    } else {
        m_currentFileSha = newSha;
        qDebug() << "✅ 上传成功！新的SHA:" << m_currentFileSha;
        
        // 持久化SHA值到本地
        QString dataPath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
        QSettings shaSettings(dataPath + "/cloud_sync_config.ini", QSettings::IniFormat);
        shaSettings.setValue("cloud/current_file_sha", m_currentFileSha);
        shaSettings.sync();
        qDebug() << "SHA已保存到本地配置";
    }
    
    // 验证：计算上传后的哈希
    QString hashAfterUpload = calculateDataHash();
    qDebug() << "上传后本地哈希:" << hashAfterUpload.left(16) << "...";
    
    // 清除修改标志
    clearModifiedFlag();
    
    emit syncSuccess("☁️ 已上传到云端");
}

// 计算当前数据的哈希值
QString SkillModel::calculateDataHash() const
{
    // 为了确保哈希稳定，我们：
    // 1. 按照技能树ID排序
    // 2. 按照技能ID排序
    // 3. 只包含核心字段，忽略UI相关字段
    
    // 获取排序后的技能树ID列表
    QStringList treeIds = m_skillTrees.keys();
    treeIds.sort();  // 字符串排序
    
    QJsonArray treesArray;
    for (const QString& treeId : treeIds) {
        const SkillTree& tree = m_skillTrees[treeId];
        
        QJsonObject treeObj;
        treeObj["id"] = tree.id.toString();
        treeObj["name"] = tree.name;
        treeObj["category"] = tree.category;
        treeObj["nextId"] = tree.nextId;  // 包含nextId
        
        // 对技能按ID排序
        QList<Skill> sortedSkills = tree.skills;
        std::sort(sortedSkills.begin(), sortedSkills.end(), 
                  [](const Skill& a, const Skill& b) { return a.id < b.id; });
        
        QJsonArray skillsArray;
        for (const Skill& skill : sortedSkills) {
            QJsonObject skillObj;
            skillObj["id"] = skill.id;
            skillObj["name"] = skill.name;
            skillObj["parent"] = skill.parent;
            // 排除 x, y（布局信息，不影响数据一致性）
            skillObj["unlocked"] = skill.unlocked;
            skillObj["completed"] = skill.completed;
            // 包含更多核心字段
            skillObj["category"] = skill.category;
            skillObj["description"] = skill.description;
            skillObj["difficulty"] = skill.difficulty;
            skillObj["progress"] = skill.progress;
            skillObj["starRating"] = skill.starRating;
            skillObj["isLevel"] = skill.isLevel;
            skillObj["maxStarRating"] = skill.maxStarRating;
            skillObj["medalIcon"] = skill.medalIcon;
            
            skillsArray.append(skillObj);
        }
        
        treeObj["skills"] = skillsArray;
        treesArray.append(treeObj);
    }
    
    QJsonObject rootObject;
    rootObject["skillTrees"] = treesArray;
    // 注意：不包含 currentTreeId，因为它只是UI状态
    
    QJsonDocument doc(rootObject);
    QByteArray jsonData = doc.toJson(QJsonDocument::Compact);
    
    // 计算SHA256哈希
    QByteArray hash = QCryptographicHash::hash(jsonData, QCryptographicHash::Sha256);
    
    qDebug() << "计算哈希的JSON前100字符:" << QString(jsonData.left(100));
    
    return QString(hash.toHex());
}

// 从JSON对象计算规范化的哈希值
QString SkillModel::calculateHashFromJson(const QJsonObject& rootObj) const
{
    // 使用与 calculateDataHash() 相同的规范化方法
    
    QJsonArray treesArray = rootObj["skillTrees"].toArray();
    
    // 创建一个临时的排序后的数组
    QList<QJsonObject> sortedTrees;
    for (const QJsonValue& treeValue : treesArray) {
        QJsonObject treeObj = treeValue.toObject();
        
        // 对技能进行排序
        QJsonArray skillsArray = treeObj["skills"].toArray();
        QList<QJsonObject> sortedSkills;
        
        for (const QJsonValue& skillValue : skillsArray) {
            sortedSkills.append(skillValue.toObject());
        }
        
        // 按ID排序技能
        std::sort(sortedSkills.begin(), sortedSkills.end(), 
                  [](const QJsonObject& a, const QJsonObject& b) {
                      return a["id"].toInt() < b["id"].toInt();
                  });
        
        // 重构技能数组，只包含核心字段（与calculateDataHash一致）
        QJsonArray normalizedSkills;
        for (const QJsonObject& skillObj : sortedSkills) {
            QJsonObject normalizedSkill;
            normalizedSkill["id"] = skillObj["id"];
            normalizedSkill["name"] = skillObj["name"];
            normalizedSkill["parent"] = skillObj["parent"];
            // 排除 x, y（布局信息）
            normalizedSkill["unlocked"] = skillObj["unlocked"];
            normalizedSkill["completed"] = skillObj["completed"];
            // 包含更多核心字段
            normalizedSkill["category"] = skillObj["category"];
            normalizedSkill["description"] = skillObj["description"];
            normalizedSkill["difficulty"] = skillObj["difficulty"];
            normalizedSkill["progress"] = skillObj["progress"];
            normalizedSkill["starRating"] = skillObj["starRating"];
            normalizedSkill["isLevel"] = skillObj["isLevel"];
            normalizedSkill["maxStarRating"] = skillObj["maxStarRating"];
            normalizedSkill["medalIcon"] = skillObj["medalIcon"];
            normalizedSkills.append(normalizedSkill);
        }
        
        // 重构技能树对象
        QJsonObject normalizedTree;
        normalizedTree["id"] = treeObj["id"];
        normalizedTree["name"] = treeObj["name"];
        normalizedTree["category"] = treeObj["category"];
        normalizedTree["nextId"] = treeObj["nextId"];  // 包含nextId
        normalizedTree["skills"] = normalizedSkills;
        
        sortedTrees.append(normalizedTree);
    }
    
    // 按技能树ID排序
    std::sort(sortedTrees.begin(), sortedTrees.end(), 
              [](const QJsonObject& a, const QJsonObject& b) {
                  return a["id"].toString() < b["id"].toString();
              });
    
    // 构建最终的规范化JSON
    QJsonArray normalizedTreesArray;
    for (const QJsonObject& tree : sortedTrees) {
        normalizedTreesArray.append(tree);
    }
    
    QJsonObject normalizedRoot;
    normalizedRoot["skillTrees"] = normalizedTreesArray;
    
    QJsonDocument doc(normalizedRoot);
    QByteArray jsonData = doc.toJson(QJsonDocument::Compact);
    
    // 计算SHA256哈希
    QByteArray hash = QCryptographicHash::hash(jsonData, QCryptographicHash::Sha256);
    
    qDebug() << "云端JSON前100字符:" << QString(jsonData.left(100));
    
    return QString(hash.toHex());
}

// 启动时检查并同步
void SkillModel::checkAndSyncOnStartup()
{
    if (!m_cloudSyncValid) {
        qDebug() << "云同步配置无效，跳过启动检查";
        return;
    }
    
    if (!isCloudSyncConfigured()) {
        qDebug() << "云同步未配置，跳过启动检查";
        return;
    }
    
    qDebug() << "启动时检查云端数据...";
    
    QString url = getCloudApiUrl() + "?access_token=" + m_giteeToken;
    QNetworkRequest request;
    request.setUrl(QUrl(url));
    request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
    
    QNetworkReply* reply = m_networkManager->get(request);
    
    connect(reply, &QNetworkReply::finished, this, [this, reply]() {
        handleStartupCheckReply(reply);
        reply->deleteLater();
    });
}

// 处理启动检查响应
void SkillModel::handleStartupCheckReply(QNetworkReply* reply)
{
    if (reply->error() != QNetworkReply::NoError) {
        qDebug() << "启动检查失败:" << reply->errorString();
        
        // 如果是404，需要先验证仓库是否存在
        if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 404) {
            qDebug() << "文件返回404，验证仓库是否存在...";
            validateRepository();
        }
        return;
    }
    
    // 解析云端数据
    QByteArray responseData = reply->readAll();
    QJsonParseError parseError;
    QJsonDocument doc = QJsonDocument::fromJson(responseData, &parseError);
    
    if (parseError.error != QJsonParseError::NoError) {
        qDebug() << "解析云端响应失败";
        return;
    }
    
    QJsonObject obj = doc.object();
    m_currentFileSha = obj["sha"].toString();
    
    // 持久化SHA值
    QString shaDataPath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
    QSettings shaSettings(shaDataPath + "/cloud_sync_config.ini", QSettings::IniFormat);
    shaSettings.setValue("cloud/current_file_sha", m_currentFileSha);
    shaSettings.sync();
    
    // 获取云端数据内容
    QString contentBase64 = obj["content"].toString();
    contentBase64.remove('\n').remove('\r').remove(' ');
    QByteArray decodedData = QByteArray::fromBase64(contentBase64.toUtf8());
    
    // 解析云端JSON，使用相同的规范化方法计算哈希
    QJsonParseError cloudParseError;
    QJsonDocument cloudDoc = QJsonDocument::fromJson(decodedData, &cloudParseError);
    
    if (cloudParseError.error != QJsonParseError::NoError) {
        qDebug() << "解析云端JSON失败，跳过哈希对比";
        return;
    }
    
    QString cloudHashStr = calculateHashFromJson(cloudDoc.object());
    
    // 计算本地数据哈希
    QString localHashStr = calculateDataHash();
    
    qDebug() << "云端哈希:" << cloudHashStr.left(16) << "...";
    qDebug() << "本地哈希:" << localHashStr.left(16) << "...";
    
    // 对比哈希值
    if (cloudHashStr != localHashStr) {
        qDebug() << "检测到云端数据不同，发送信号";
        emit cloudDataDifferent(cloudHashStr, localHashStr);
    } else {
        qDebug() << "云端和本地数据一致";
    }
}

// 退出时检查并同步
void SkillModel::checkAndSyncOnExit()
{
    qDebug() << "========== 退出检查被调用 ==========";
    
    if (!m_cloudSyncValid) {
        qDebug() << "云同步配置无效，跳过退出检查";
        return;
    }
    
    if (!isCloudSyncConfigured()) {
        qDebug() << "云同步未配置，跳过退出检查";
        return;
    }
    
    if (m_skillTrees.isEmpty()) {
        qDebug() << "本地数据为空，跳过退出检查";
        return;
    }
    
    qDebug() << "退出时检查本地数据，开始网络请求...";
    
    QString url = getCloudApiUrl() + "?access_token=" + m_giteeToken;
    QNetworkRequest request;
    request.setUrl(QUrl(url));
    request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
    
    QNetworkReply* reply = m_networkManager->get(request);
    
    connect(reply, &QNetworkReply::finished, this, [this, reply]() {
        qDebug() << "退出检查网络请求完成";
        handleExitCheckReply(reply);
        reply->deleteLater();
    });
}

// 处理退出检查响应
void SkillModel::handleExitCheckReply(QNetworkReply* reply)
{
    if (reply->error() != QNetworkReply::NoError) {
        qDebug() << "退出检查失败:" << reply->errorString();
        
        // 如果是404，说明云端没有文件，提示用户上传
        if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 404) {
            qDebug() << "云端文件不存在，提示用户上传";
            // 发送信号，让用户选择是否上传
            emit localDataDifferent("local_exists", "cloud_not_exists");
        }
        return;
    }
    
    // 解析云端数据
    QByteArray responseData = reply->readAll();
    QJsonParseError parseError;
    QJsonDocument doc = QJsonDocument::fromJson(responseData, &parseError);
    
    if (parseError.error != QJsonParseError::NoError) {
        qDebug() << "解析云端响应失败";
        return;
    }
    
    QJsonObject obj = doc.object();
    m_currentFileSha = obj["sha"].toString();
    
    // 持久化SHA值
    QString shaDataPath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
    QSettings shaSettings(shaDataPath + "/cloud_sync_config.ini", QSettings::IniFormat);
    shaSettings.setValue("cloud/current_file_sha", m_currentFileSha);
    shaSettings.sync();
    
    // 获取云端数据内容
    QString contentBase64 = obj["content"].toString();
    contentBase64.remove('\n').remove('\r').remove(' ');
    QByteArray decodedData = QByteArray::fromBase64(contentBase64.toUtf8());
    
    // 解析云端JSON，使用相同的规范化方法计算哈希
    QJsonParseError cloudParseError;
    QJsonDocument cloudDoc = QJsonDocument::fromJson(decodedData, &cloudParseError);
    
    if (cloudParseError.error != QJsonParseError::NoError) {
        qDebug() << "解析云端JSON失败，跳过哈希对比";
        return;
    }
    
    QString cloudHashStr = calculateHashFromJson(cloudDoc.object());
    
    // 计算本地数据哈希
    QString localHashStr = calculateDataHash();
    
    qDebug() << "云端哈希:" << cloudHashStr.left(16) << "...";
    qDebug() << "本地哈希:" << localHashStr.left(16) << "...";
    
    // 对比哈希值
    if (localHashStr != cloudHashStr) {
        qDebug() << "检测到本地数据不同，发送信号";
        emit localDataDifferent(localHashStr, cloudHashStr);
    } else {
        qDebug() << "本地和云端数据一致，发送一致信号";
        emit dataConsistent();
    }
}

// 标记数据已修改
void SkillModel::markDataAsModified()
{
    if (!m_dataModified) {
        m_dataModified = true;
        qDebug() << "🔔 数据已标记为修改状态";
        emit dataModifiedChanged();  // 通知QML属性改变
    }
}

// 检查数据是否已修改
bool SkillModel::hasDataModified() const
{
    return m_dataModified;
}

// 检查云同步配置是否有效
bool SkillModel::isCloudSyncValid() const
{
    return m_cloudSyncValid;
}

// 清除修改标志
void SkillModel::clearModifiedFlag()
{
    if (m_dataModified) {  // 只有当状态真正改变时才发出信号
        m_dataModified = false;
        qDebug() << "✅ 修改标志已清除";
        emit dataModifiedChanged();  // 通知QML属性改变
    }
}

void SkillModel::updatePlayerStats()
{
    int totalPoints = 0;
    int totalStars = 0;

    for (auto it = m_skillTrees.constBegin(); it != m_skillTrees.constEnd(); ++it) {
        const SkillTree& tree = it.value();
        for (const Skill& skill : tree.skills) {
            if (skill.completed) {
                totalPoints += 1;
                totalStars += std::max(0, skill.starRating);
            }
        }
    }

    emit playerStatsUpdated(totalPoints, totalStars);
}
